#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/shm.h>
#include <sys/wait.h>

int main(int argc, char const *argv[]);
void *Tcp_Server_func(void *arg);

// 定义一个全局变量的数组
typedef struct tpc_Sock_fds
{
    int Sock_fd;
    char name[128];
} tpc_Sock_fds;

#define MAX_LIST 20


typedef struct tpc_sql_list
{
    tpc_Sock_fds data[MAX_LIST];
    int len;
}tpc_sql_list;




int main(int argc, char const *argv[])
{
    // 判断命令行参数
    if (argc < 2)
    {
        printf("参数错误:%s ip port\n", argv[0]);
        return -1;
    }
    // 给定默认ip

    char ip_str[128];

    if (3 != argc)
    {

        strcpy(ip_str, "10.0.0.63");
    }
    else
    {

        strcpy(ip_str, argv[2]);
    }

    //  1、创建套接字
    int Sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == Sock_fd)
    {
        perror("socket erron");
        return -1;
    }

    //  2、填充服务器网络信息结构体
    struct sockaddr_in Sock_addr;
    Sock_addr.sin_family = AF_INET;                // 选择 IPV4
    Sock_addr.sin_port = htons(atoi(argv[1]));     // 给定端口 // atoi 将字符串 转换成数字
    Sock_addr.sin_addr.s_addr = inet_addr(ip_str); // IP地址

    socklen_t Sock_size = sizeof(Sock_addr);

    //  3、将套接字与服务器的网络信息结构体绑定
    if (-1 == bind(Sock_fd, (struct sockaddr *)&Sock_addr, Sock_size))
    {
        perror("bind err");
        return -1;
    }

    //  4、将套接字设置城 监听状态

    if (-1 == listen(Sock_fd, 20))
    {
        perror("Sock_fd err");
        return -1;
    }

    printf("有用户链接");

    pid_t Pid; // 用于存放IP

    int uid = 0;

    int index = 0;

    int shmid = 5;
    
    // 映射 共享内存 到 当前进程

    tpc_sql_list * list = (tpc_sql_list *)shmat(shmid, NULL, 0);
    list->len = 0;

    memset(list , 0 , sizeof(tpc_sql_list));


    while (1)
    {

        //  5、阻塞等待客户端连接 accept
        int acc_fd = accept(Sock_fd, NULL, NULL);
        if (-1 == acc_fd)
        {
            perror("accept err");
            return -1;
        }

        if (list->len >= MAX_LIST)
        {
            printf("已经满了\n");
            send(acc_fd, "已经满了", strlen("已经满了"), 0);
            continue;
        }

        // 更新顺序表

        list->data[list->len].Sock_fd = acc_fd;  // 增加顺序表 的 套接字
        sprintf( list->data[index].name , "新用户%d" , uid++);


        index = list->len;
        list->len++;
 
        printf("name = %s fd = %d\n index = %d" , list->data[list->len].name , list->len,index);
        
        // 创建进程
        Pid = fork();

        if (0 == Pid) // 子进程
        {
            // 分离内存
           shmdt(list);
            // 传递共享内存的地址
            Tcp_Server_func((int *)&index);

        }
        if (0 < Pid) // 父进程
        {
            close(acc_fd);
        }

        
    }

    //  7、关闭套接字  close
    close(Sock_fd);


    return 0;
}

void *Tcp_Server_func(void *arg)
{
    // 声明变量
    int index = *((int *)arg);
    char name[128];
    int Sock_fd;

    int shmid = 5;

    char buf[1024];
    char send_buf[2048];

    // 映射 共享内存 到 当前进程
  tpc_sql_list * list = (tpc_sql_list *)shmat(shmid, NULL, 0);
    if (list == (void *)-1) {
        perror("shmat error");
        return NULL;
    }
    Sock_fd = list->data[index].Sock_fd;
    printf("naem:%s\n", list->data[index].name);
    strcpy( name , list->data[index].name );


    while (1)
    {
        memset(buf, 0, sizeof(buf));

        recv(Sock_fd, buf, sizeof(buf), 0);

        // 修改名字
        if (NULL != strstr(buf, "set_name_"))
        {
            // 写入姓名
            strcpy( list->data[index].name , buf + 9);
            sprintf(buf, "修改名称成功:%s", list->data[index].name );
            send(Sock_fd, buf, strlen(buf), 0);
            
        }
        // 退出连接
        else if (!strcmp(buf, "quit"))
        {
            // 组装
            sprintf(send_buf, "%s : %s", list->data[index].name , "退出成功");
            send(Sock_fd, send_buf, strlen(send_buf), 0);
            break;
        }

        // 转发 群发
        else
        {
            // 发送给其他成员
            for (size_t i = 0; i < list->len; i++)
            {
                // 组装字符串
                sprintf(send_buf, "%s : %s", list->data[index].name, buf);

                if (Sock_fd != list->data[i].Sock_fd)
                {
                    printf("fd = [%d] name = [%s]\n" , list->data[i].Sock_fd , list->data[i].name);
                    send( list->data[i].Sock_fd , send_buf, strlen(send_buf), 0);
                }
            }
        }
    }

    // 退出  关闭文件描述符   全局顺序表 就需要做删除  len --
    close(Sock_fd);

    // 从顺序表中删除

    for (size_t i = index; i < MAX_LIST; i++)
    {
        list->data[i] = list->data[i + 1];
    }

    list->len--;

    printf("链接已退出");

    return NULL;
}
